home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / dskut / dlcpmv23.zip / CPMV.C < prev    next >
C/C++ Source or Header  |  1992-03-29  |  20KB  |  721 lines

  1. /*************************************************************************
  2. * DL_CPMV v2.2 92/02/15 by Dennis Lo
  3. *     This program and its source code are in the public domain.
  4. *
  5. * DESCRIPTION:
  6. *    DL_CPMV is a BSD Unix-like cp and mv for MS-DOS.  Unlike most 
  7. *    other MS-DOS cp and mv programs, this one:
  8. *        - has the -p and -v options from BSD/SunOS.
  9. *        - can move files across drives.
  10. *        - can rename directories as well as files
  11. *        - supports environment variable references in file names
  12. *        - allows forward slashes to be used in path names 
  13. *
  14. * TO COMPILE:
  15. *    The same source file contains both MV and CP.  To compile, #define 
  16. *    either CP or MV below, rename this file to cp.c or mv.c, and compile
  17. *    with Turbo C++ v1.01, small memory model:
  18. *        eg.    tcc cp.c     or     
  19. *            tcc mv.c
  20. *    Other versions of Turbo C should also work but have not been tested.
  21. *    User documentation is embedded in HelpExit().
  22. *    Notes: a "filename" means something like "c:/etc/zansi.sys".  A 
  23. *    "filespec" means something like "c:/etc/*.sys".
  24. *
  25. * TEST SUITE:
  26. *    - mv/cp within current dir:        mv mv.obj mv.2
  27. *    - mv/cp from current dir to other dir:    mv mv.obj ..
  28. *    - mv/cp from other dir to another dir:    mv c:/d/work/mv.obj /tmp/mv.o
  29. *    - mv/cp across drives:            mv mv.obj e:
  30. *    - mv/cp across drives with paths:    mv c:/d/work/mv.obj e:/tmp
  31. *    - test -f/-i flags            cp -p cp.obj mv.obj
  32. *    - test -p/-n flags:            mv -p mv.obj a:
  33. *    - test -v/-q flags:            mv -q mv.obj mv.2
  34. *    - test several flags together:        mv -ipn mv.obj mv.2
  35. *    - test flags in env variable:        set MV=piv
  36. *    - mv/cp -r across drives        mv -r c:/tmp/1 e:/
  37. *    - mv/cp -r on same drive        mv -r /tmp/1/*.* /tmp/2
  38. *    - mv/cp no -r: subdirs not moved    mv *.* ..
  39. *
  40. * CHANGE LOG:
  41. *    Version 2.3 92/03/30:
  42. *    - return error code of 0 when successful 
  43. *    Version 2.2 92/01/08:
  44. *    - fixed cp -r bug that tries to remove subdirectories
  45. *    Version 2.1 92/01/01:
  46. *    - added -r option
  47. *    - added explicit wildcard expansion to allow cases like $junk/*.*
  48. *    Version 2.0 91/12/28:
  49. *    - switched from Datalight C to Turbo C++ (and the .exe grew by 5K !)
  50. *    - mv defaults to -p instead of -n
  51. *    - mv can rename directories now
  52. *    Version 1.6 91/01/20:
  53. *    - allow moving across drives
  54. * TODO:
  55. *    - Switch disks if src<>dst drive, and dest drive is a floppy, and
  56. *      dest free space < current file size
  57. *    - package for release
  58. *************************************************************************/
  59. /* 
  60.  * Define one of the following to indicate whether this is 'mv' or 'cp' 
  61.  */
  62. #define CP 
  63. /* 
  64. #define MV 
  65. */
  66.  
  67. #include <io.h>
  68. #include <fcntl.h>
  69. #include <sys/stat.h>
  70. #include <stdio.h>
  71. #include <dos.h>
  72. #include <dir.h>
  73. #include <conio.h>
  74. #include <stdlib.h>
  75.  
  76. /*=====================================================================
  77.  * Macros
  78.  *=====================================================================
  79.  */
  80. /* Macro to write a string to stdout (avoids the need to use printf) */
  81. #define PUT(str) fputs (str, stdout)
  82.  
  83. /*=====================================================================
  84.  * Global Variables
  85.  *=====================================================================
  86.  */
  87. int Force_flag = 1;
  88. int Verbose_flag = 0;
  89. int Recursive_flag = 0;
  90. #ifdef CP
  91.     int Preserve_flag = 0;    /* CP: use new timestamp by default */
  92. #else
  93.     int Preserve_flag = 1;    /* MV: preserve timestamp by default */
  94. #endif
  95.  
  96. /*=====================================================================
  97.  * Print a help message and exit
  98.  *=====================================================================
  99.  */
  100. void
  101. HelpExit ()
  102. {
  103. #ifdef CP
  104.     PUT ("DL_CP - a BSD Unix style file copy utility   v2.3 92/03/30 Dennis Lo\n");
  105.     PUT ("USAGE: cp  [-fipnvq] file dest_file\n");    
  106.     PUT ("   or: cp  [-fipnvq] file1 [file2...fileN] dest_directory\n");    
  107.     PUT ("  -f = Force overwrites without prompting (default).\n");
  108.     PUT ("  -i = Interactively prompt before overwriting (opposite of -f).\n");
  109.     PUT ("  -p = Preserve original timestamp when copying.\n");
  110.     PUT ("  -n = use New timestamp when copying (opposite of -p). (default)\n");
  111.     PUT ("  -v = Verbose - show each file copied.\n");
  112.     PUT ("  -q = Quiet - opposite of -v (default).\n");
  113.     PUT ("  -r = Recursively descend & copy sub-directories.\n");
  114.     PUT ("Default arguments can be put in the CP environment variable. (eg. 'set CP=ipv')\n");
  115.     PUT ("File names can contain environment variable references (eg. 'cp $work/*.c a:')\n");
  116. #else
  117.     PUT ("DL_MV - a BSD Unix style file move utility   v2.3 92/03/30 Dennis Lo\n");
  118.     PUT ("USAGE: mv  [-fipnvq] old_file new_file\n");    
  119.     PUT ("   or: mv  [-fipnvq] file1 [file2...fileN] dest_directory\n");
  120.     PUT ("  -f = Force overwrites without prompting (default).\n");
  121.     PUT ("  -i = Interactively prompt before overwriting (opposite of -f).\n");
  122.     PUT ("  -p = Preserve original timestamp when copying. (default)\n");
  123.     PUT ("  -n = use New timestamp when copying (opposite of -p).\n");
  124.     PUT ("  -v = Verbose - show each file copied.\n");
  125.     PUT ("  -q = Quiet - opposite of -v (default).\n");
  126.     PUT ("  -r = Recursively descend & move sub-directories.\n");
  127.     PUT ("Can move directories.  Can move files from one drive to another.\n");
  128.     PUT ("Default arguments can be put in the MV environment variable. (eg. 'set MV=ipv')\n");
  129.     PUT ("File names can contain environment variable references (eg. 'mv $work/*.c a:')\n");
  130. #endif
  131.     exit (1);
  132. }
  133.  
  134. /*=====================================================================
  135.  * Return TRUE if a filespec is a file
  136.  *=====================================================================
  137.  */
  138. IsFile (filespec)
  139.     char *filespec;
  140. {
  141.     struct ffblk findblk;
  142.  
  143.     return (findfirst (filespec, &findblk, 0) == 0);
  144.     /* maybe should change the 0 to FA_HIDDEN|FA_SYSTEM */
  145. }
  146.  
  147. /*=====================================================================
  148.  * Return TRUE if a filespec is a directory
  149.  *=====================================================================
  150.  */
  151. IsDir (filespec)
  152.     char *filespec;
  153. {
  154.     struct ffblk findblk;
  155.     int lastchar = filespec [strlen (filespec) - 1];
  156.  
  157.     return ((findfirst (filespec, &findblk, FA_DIREC) == 0  
  158.          &&  !IsFile (filespec))
  159.         || lastchar == '.'  ||  lastchar == '/'  ||  lastchar == '\\');
  160. }
  161.  
  162. /*=====================================================================
  163.  * Interpret an arguments string of the form "fiv" (not "-f -iv")
  164.  *=====================================================================
  165.  */
  166. void
  167. ParseArgs (str)
  168.     char *str;
  169. {
  170.     char *param;
  171.     static char errmsg[] = "-?\n";
  172.  
  173.     for (param = str;  *param;    param++)
  174.     {
  175.     switch (*param)
  176.     {
  177.         case 'f':            /* Force overwrites */
  178.         Force_flag = 1;
  179.         break;
  180.         case 'i':            /* Interactive */
  181.         Force_flag = 0;
  182.         break;
  183.         case 'n':            /* cp: create New file */
  184.         Preserve_flag = 0;
  185.         break;
  186.         case 'p':            /* cp: Preserve timestamp & modes */
  187.         Preserve_flag = 1;
  188.         break;
  189.         case 'q':            /* Quiet */
  190.         Verbose_flag = 0;
  191.         break;
  192.         case 'r':            /* Recursive */
  193.         Recursive_flag = FA_DIREC;
  194.         break;
  195.         case 'v':            /* Verbose */
  196.         Verbose_flag = 1;
  197.         break;
  198.         default:
  199.         errmsg[1] = *param;
  200.         PUT ("Invalid parameter "); PUT (errmsg);
  201.         HelpExit ();
  202.     }
  203.     }
  204. }
  205.  
  206. /*=====================================================================
  207.  * Return the drive letter of a filename
  208.  *=====================================================================
  209.  */
  210. char
  211. DriveName (filename)
  212.     char *filename;
  213. {
  214.     char drive;
  215.  
  216.     /* 
  217.      * If the drive name is embedded in the given file name then
  218.      * extract it
  219.      */
  220.     if (filename[1] == ':')
  221.     {
  222.     drive = filename[0];
  223.     }
  224.     /* else return current drive */
  225.     else 
  226.     {
  227.     char path [80];
  228.     getcwd (path, 79);
  229.     drive = *path;
  230.     }
  231.     return (toupper (drive));
  232. }
  233.  
  234. /*=====================================================================
  235.  * Return a pointer to the filename portion of a filespec.
  236.  * (Skips the drive specifier and the path)
  237.  *=================================